import { fromTemplate } from '$lib/builder';
import { Creature, Description, Glyph, Impassable } from '$lib/components';
import { Ground } from '$lib/content/ground';
import { hasComponent, type IWorld } from '$lib/ecs';
import { Landscape } from '$lib/landscape';
import { onTile, placeOnMap } from '$lib/pos3';
import { random } from '$lib/random';
import { Entrance } from './door';
import { thugTemplate } from './thug';

export const floorTemplate = [
  [Ground],
  [Landscape],
  [Glyph, {
    glyph: '·',
    color: '#8888',
  }],
  [Description, {
    summary: "floor",
    description: "Nothing fancy, it's just a floor.",
  }],
];

export const fillRectangle = (world, tmplt, x0, y0, x1, y1, z) => {
  for (let x = x0; x <= x1; ++x) {
    for (let y = y0; y <= y1; ++y) {
      const e = fromTemplate(world, tmplt);
      placeOnMap(world, e, x, y, z);
    }
  }
};

export const drawRectangle = (world, tmplt, x0, y0, x1, y1, z) => {
  const width = x1-x0;
  const height = y1-y0;
  for (let side = 0; side < 4; ++side) {
    for (let i = 0; i < [width, height][side % 2]; ++i) {
      const x = i * (side === 0) + x1 * (side === 1 || side === 2) - i * (side === 2);
      const y = i * (side === 1) + y1 * (side > 1) - i * (side === 3);
      const e = fromTemplate(world, tmplt);
      placeOnMap(world, e, x, y, z);
    }
  }
};

export const spawnNearby = (world, tmplt, x, y, z) => {
  const e = fromTemplate(world, tmplt);
  let placed = false;
  let r = 2;
  let retry = 0;
  while (!placed && r < 5) {
    const dx = Math.floor(r * random());
    const dy = Math.floor(r * random());
    const y1 = y+dy;
    const x1 = x+dx;
    if (canPlace(world, x1, y1, z)) {
      placeOnMap(world, e, x1, y1, z);
      placed = true;
    }
    if (++retry > 16) {
      retry = 0;
      ++r;
    }
  }
  if (!placed)
    throw new Error("Couldn't spawn nearby!");
};

export type VaultOptions = {
  toReturn?: string,
  anchor?: [number, number],
};

export const vault = (tmplt: string, mapping, options: VaultOptions) => {
  const glyphs = tmplt.split('\n').filter((c) => c !== '');
  const width = glyphs[0].length;
  const height = glyphs.length;
  const spawn = (world, x0, y0, z) => {
    let result;
    glyphs.forEach((line, dy) => {
      const y = y0+dy;
      line.split('').forEach((glyph, dx) => {
        const x = x0+dx;
        if (mapping[glyph]) {
          const e = fromTemplate(world, mapping[glyph]);
          placeOnMap(world, e, x, y, z);
          const r = options.toReturn?.indexOf(glyph);
          if (r !== undefined && r >= 0) {
            result = e;
          }
        }
      });
    });
    return result;
  }
  return {
    width,
    height,
    spawn,
    anchor: options.anchor ?? [0, 0],
  };
};

export const spawnVault = (world, tmplt, x0, y0, z) => {
  const [dx0, dy0] = tmplt.anchor;
  const x = x0 - dx0;
  const y = y0 - dy0;
  let placeOk = true;
  for (let dy = 0; dy < tmplt.height; ++dy) {
    for (let dx = 0; dx < tmplt.width; ++dx) {
      if (!canPlace(world, x + dx, y + dy, z)) {
        console.warn(`can't place on ${x} ${y}`);
        placeOk = false;
        break;
      }
    }
  }
  if (placeOk) {
    return tmplt.spawn(world, x, y, z);
  }
  throw new Error(`Couldn't place vault at {x0} {y0} {z}`);
};

export const spawnVaultRandom = (world, tmplt, x0, y0, width, height, z) => {
  let placed = false;
  let retry = 0;
  let result;
  while (!placed && retry < 256) {
    const x = x0 + Math.floor(random()*(width-tmplt.width));
    const y = y0 + Math.floor(random()*(height-tmplt.height));
    let placeOk = true;
    for (let dy = 0; dy < tmplt.height; ++dy) {
      for (let dx = 0; dx < tmplt.width; ++dx) {
        if (!canPlace(world, x+dx, y+dy, z)) {
          console.warn(`can't place on ${x} ${y}`);
          placeOk = false;
          break;
        }
      }
    }
    if (placeOk) {
      result = tmplt.spawn(world, x, y, z);
      placed = true;
    }
    ++retry;
  }
  if (!placed)
    throw new Error("Couldn't place a vault!");
  return result;
};

export const spawnThug = (world, z, x0, y0, width, height) => {
  let retry = 16;
  let placed = false;
  while (!placed && retry > 0) {
    const x = x0 + Math.floor(random()*width);
    const y = y0 + Math.floor(random()*height);
    if (canPlace(world, x, y, z)) {
      const e = fromTemplate(world, thugTemplate);
      placeOnMap(world, e, x, y, z);
    }
    --retry;
  }
  return placed;
};

export const spawnRandomThugs = (world, z, width, height, count) => {
  for (let i = 0; i < count; ++i) {
    const res = spawnThug(world, z, 0, 0, width, height);
    if (!res) {
      console.warn("Couldn't spawn a thug!");
    }
  }
};

const canPlace = (world: IWorld, x, y, z) => {
  const es = onTile(world, x, y, z);
  const hasGround = es.some((e) => hasComponent(world, Ground, e));
  const isImpassable = es.some((e) => hasComponent(world, Impassable, e));
  const hasCreature = es.some((e) => hasComponent(world, Creature, e));
  const isPlayerSpawn = es.some((e) => hasComponent(world, Entrance, e));
  return hasGround && !isImpassable && !hasCreature && !isPlayerSpawn;
};
